from pydantic import BaseModel, conlist
from pydantic.generics import GenericModel
from datetime import datetime
from typing import TypeVar, Generic, Optional, Union
from settings import settings
from .modem_state import ModemState


T = TypeVar('T')

class TSValue(GenericModel, Generic[T]):
    value: T
    subgroup: str = 'invalid'
    is_need: bool = False
    is_incorrect: bool = False
    is_incorrect_user: bool = False
    is_incorrect_server: bool = False
    measurement_name: Optional[str] = None

class BaseTechnicalStatus(BaseModel):
    def mark_is_invalid_from_user(self):
        for field in self.__fields_set__:
            try:
                field = getattr(self, field)
                field.is_incorrect = field.is_incorrect_user
            except Exception:
                pass

    def mark_is_invalid_from_server(self):
        for field in self.__fields_set__:
            try:
                field = getattr(self, field)
                field.is_incorrect = field.is_incorrect_server
            except Exception:
                pass

class Statuses(BaseTechnicalStatus):
    ad9361_1: TSValue[float] = TSValue[float](value=1.0)
    ad9361_2: TSValue[float] = TSValue[float](value=1.0)
    ad9361_3: TSValue[float] = TSValue[float](value=1.0)
    ad9364_4: TSValue[float] = TSValue[float](value=1.0)
    modem_no_sim: TSValue[bool] = TSValue[bool](value=False)
    modem_no_work: TSValue[bool] = TSValue[bool](value=False)
    modem_no_work_count: TSValue[int] = TSValue[int](value=0)
    modem_no_sim_count: TSValue[int] = TSValue[int](value=0)
    server: TSValue[bool] = TSValue[bool](value=False)
    kintex_live: TSValue[float] = TSValue[float](value=1.0)
    ad9361_1_invalid: TSValue[bool] = TSValue[bool](value=False)
    ad9361_2_invalid: TSValue[bool] = TSValue[bool](value=False)
    ad9361_3_invalid: TSValue[bool] = TSValue[bool](value=False)
    ad9364_invalid: TSValue[bool] = TSValue[bool](value=False)

class TSOcbStates(BaseTechnicalStatus):
    link_0_ocb: TSValue[bool] = TSValue[bool](value=False)
    link_1_ocb: TSValue[bool] = TSValue[bool](value=False)
    service_0_ocb: TSValue[bool] = TSValue[bool](value=False)
    service_1_ocb: TSValue[bool] = TSValue[bool](value=False)
    service_2_ocb: TSValue[bool] = TSValue[bool](value=False)
    service_3_ocb: TSValue[bool] = TSValue[bool](value=False)
    service_5v_ocb: TSValue[bool] = TSValue[bool](value=False)


class TSSfpModule(BaseTechnicalStatus):
    rx_los: TSValue[bool] = TSValue[bool](value=False, is_need=True)
    rx_power_dbm: TSValue[Union[int, str]] = TSValue[Union[int, str]](value=1, is_need=True, measurement_name='dBm')
    tx_fault: TSValue[bool] = TSValue[bool](value=False, is_need=True)
    tx_power_watt: TSValue[Union[float, str]] = TSValue[Union[float, str]](value=1, is_need=True, measurement_name='mW')
    mod_abs: TSValue[bool] = TSValue[bool](value=False, is_need=True)


class TSGpsTelemetry(BaseTechnicalStatus):
    status: TSValue[bool] = TSValue[bool](value=False, is_need=True)
    bs_sync: TSValue[bool] = TSValue[bool](value=False, is_need=True)
    timestamp: TSValue[int] = TSValue[datetime](value=datetime.utcnow().timestamp(), is_need=True)
    latitude: TSValue[float] = TSValue[float](value=0.0, is_need=True)
    north_south: TSValue[str] = TSValue[str](value='N', is_need=True)
    longitude: TSValue[float] = TSValue[float](value=0.0, is_need=True)
    east_west: TSValue[str] = TSValue[str](value='E', is_need=True)
    reference_freq_40mhz: TSValue[float] = TSValue[float](value=4e7)
    last_capture_time: TSValue[Union[bool, int]] = TSValue[Union[bool, int]](value=False, is_need=True)
    last_bs_capture_time: TSValue[Union[bool, int]] = TSValue[Union[bool, int]](value=False, is_need=True)
    time_type: TSValue[str] = TSValue[str](value="None", is_need=True)


class TSPwrTelemetry(BaseTechnicalStatus):
    current48v: TSValue[float] = TSValue[float](value=0.0, subgroup='current', is_need=True, measurement_name='A')
    current28v: TSValue[float] = TSValue[float](value=0.0, subgroup='current', is_need=True, measurement_name='A')
    current5v0: TSValue[float] = TSValue[float](value=0.0, subgroup='current', is_need=True, measurement_name='A')
    temperature5v: TSValue[float] = TSValue[float](value=0.0, subgroup='temperature', is_need=True, measurement_name='ºC')
    voltage28v: TSValue[float] = TSValue[float](value=0.0, subgroup='voltage', is_need=True, measurement_name='V')
    voltage5v: TSValue[float] = TSValue[float](value=0.0, subgroup='voltage', is_need=True, measurement_name='V')


class TSDspTelemetry(BaseTechnicalStatus):
    cap_out_v: TSValue[float] = TSValue[float](value=0.0, subgroup='voltage', is_need=True, measurement_name='V')

    fpga_1v8: TSValue[float] = TSValue[float](value=0.0, subgroup='voltage', is_need=True, measurement_name='V')
    fpga_3v3: TSValue[float] = TSValue[float](value=0.0, subgroup='voltage', is_need=True, measurement_name='V')

    h3_vdd_3v3: TSValue[float] = TSValue[float](value=0.0)

    temperature_40mhz: TSValue[float] = TSValue[float](value=0.0, subgroup='temperature', is_need=True, measurement_name='ºC')
    temperature_ad9364: TSValue[float] = TSValue[float](value=0.0, subgroup='temperature', is_need=True, measurement_name='ºC')
    temperature_link_ad9361: TSValue[float] = TSValue[float](value=0.0, subgroup='temperature', is_need=True, measurement_name='ºC')
    temperature_fpga: TSValue[float] = TSValue[float](value=0.0, subgroup='temperature', is_need=True, measurement_name='ºC')
    temperature_h3: TSValue[float] = TSValue[float](value=0.0)
    temperature_sv1_ad9361: TSValue[float] = TSValue[float](value=0.0, subgroup='temperature', is_need=True, measurement_name='ºC')
    temperature_sv2_ad9361: TSValue[float] = TSValue[float](value=0.0, subgroup='temperature', is_need=True, measurement_name='ºC')

    vbat_mpcie: TSValue[float] = TSValue[float](value=0.0, subgroup='voltage', is_need=True, measurement_name='V')

    vdc1_1v3_synth: TSValue[float] = TSValue[float](value=0.0, subgroup='voltage', is_need=True, measurement_name='V')
    vdc1_1v3_xcvr: TSValue[float] = TSValue[float](value=0.0, subgroup='voltage', is_need=True, measurement_name='V')
    vdc1_1v8: TSValue[float] = TSValue[float](value=0.0, subgroup='voltage', is_need=True, measurement_name='V')

    vdc2_1v3_synth: TSValue[float] = TSValue[float](value=0.0, subgroup='voltage', is_need=True, measurement_name='V')
    vdc2_1v3_xcvr: TSValue[float] = TSValue[float](value=0.0, subgroup='voltage', is_need=True, measurement_name='V')
    vdc2_1v8: TSValue[float] = TSValue[float](value=0.0, subgroup='voltage', is_need=True, measurement_name='V')

    vdc3_1v3_synth: TSValue[float] = TSValue[float](value=0.0, subgroup='voltage', is_need=True, measurement_name='V')
    vdc3_1v3_xcvr: TSValue[float] = TSValue[float](value=0.0, subgroup='voltage', is_need=True, measurement_name='V')
    vdc3_1v8: TSValue[float] = TSValue[float](value=0.0, subgroup='voltage', is_need=True, measurement_name='V')

    vdc4_1v3_synth: TSValue[float] = TSValue[float](value=0.0, subgroup='voltage', is_need=True, measurement_name='V')
    vdc4_1v3_xcvr: TSValue[float] = TSValue[float](value=0.0, subgroup='voltage', is_need=True, measurement_name='V')
    vdc4_1v8: TSValue[float] = TSValue[float](value=0.0, subgroup='voltage', is_need=True, measurement_name='V')


class TSSmTelemetry(BaseTechnicalStatus):
    is_rev_1_2: TSValue[bool] = TSValue[float](value=False)
    is_rev_1_0: TSValue[bool] = TSValue[float](value=False)
    temperature: TSValue[float] = TSValue[float](value=0.0, is_need=True, measurement_name='ºC')
    voltage_28v: TSValue[float] = TSValue[float](value=0.0, is_need=True, measurement_name='V')
    current_28v: TSValue[float] = TSValue[float](value=0.0, is_need=True, measurement_name='A')
    preamp_5v: TSValue[float] = TSValue[float](value=0.0)
    lna_5v: TSValue[float] = TSValue[float](value=0.0, is_need=True, measurement_name='V')
    direct_power: TSValue[float] = TSValue[float](value=0.0)
    reflected_power: TSValue[float] = TSValue[float](value=0.0)
    bit_settings: TSValue[float] = TSValue[float](value=0.0)
    crc: TSValue[float] = TSValue[float](value=0.0)
    gain: TSValue[float] = TSValue[float](value=0.0)
    attenuation: TSValue[float] = TSValue[float](value=0.0)
    up_link_power: TSValue[float] = TSValue[float](value=0.0, is_need=True)
    down_link_power: TSValue[float] = TSValue[float](value=0.0, is_need=True)
    base_station_current_power: TSValue[float] = TSValue[float](value=0.0, is_need=True, measurement_name='dBm')
    sm_output_power: TSValue[float] = TSValue[float](value=0.0, is_need=True, measurement_name='dBm')
    ocb_in_work: TSValue[bool] = TSValue[bool](value=False)
    pap: TSValue[float] = TSValue[float](value=0.0, is_need=True, measurement_name='dBpd')


class TSRmTelemetry(BaseTechnicalStatus):
    temperature: TSValue[float] = TSValue[float](value=0.0, is_need=True, measurement_name='ºC')
    voltage_28v: TSValue[float] = TSValue[float](value=0.0, is_need=True, measurement_name='V')
    voltage_8v: TSValue[float] = TSValue[float](value=0.0, is_need=True, measurement_name='V')
    voltage_5v: TSValue[float] = TSValue[float](value=0.0, is_need=True, measurement_name='V')
    preamp_current: TSValue[float] = TSValue[float](value=0.0)
    pa_current: TSValue[float] = TSValue[float](value=0.0)
    rf_direct_detector_out: TSValue[float] = TSValue[float](value=0.0)
    rf_rev_detector_out: TSValue[float] = TSValue[float](value=0.0)
    rx_synth_lock: TSValue[bool] = TSValue[bool](value=False)
    tx_synth_lock: TSValue[bool] = TSValue[bool](value=False)
    current_rm_module: TSValue[float] = TSValue[float](value=0.0, is_need=True, measurement_name='A')
    gain: TSValue[float] = TSValue[float](value=0.0)
    attenuation: TSValue[float] = TSValue[float](value=0.0)
    up_link_power: TSValue[float] = TSValue[float](value=0.0, is_need=True)
    down_link_power: TSValue[float] = TSValue[float](value=0.0, is_need=True)
    pilots_power_rx: TSValue[float] = TSValue[float](value=0.0, is_need=True, measurement_name='dB')
    pilots_power_tx: TSValue[float] = TSValue[float](value=0.0)
    pilots_power_tx_link_status: TSValue[bool] = TSValue[bool](value=False)
    pilots_power_rx_link_status: TSValue[bool] = TSValue[bool](value=False)
    rtr_normal_vswr: TSValue[float] = TSValue[float](value=0.0)


class TechnicalStatus(BaseModel):
    rm_telemetry: conlist(
        TSRmTelemetry,
        min_items=settings.SERVICES_RELAYS_NUMBER,
        max_items=settings.SERVICES_RELAYS_NUMBER
    ) = [TSRmTelemetry() for _ in range(settings.SERVICES_RELAYS_NUMBER)]
    sm_telemetry: conlist(
        TSSmTelemetry,
        min_items=settings.SERVICES_MODULE_NUMBER,
        max_items=settings.SERVICES_MODULE_NUMBER
    ) = [TSSmTelemetry() for _ in range(settings.SERVICES_MODULE_NUMBER)]

    dsp_telemetry: TSDspTelemetry = TSDspTelemetry()
    pwr_telemetry: TSPwrTelemetry = TSPwrTelemetry()
    gps_telemetry: TSGpsTelemetry = TSGpsTelemetry()
    sfp_telemetry: conlist(
        TSSfpModule,
        min_items=2,
        max_items=2
    ) = [TSSfpModule(), TSSfpModule()]
    ocb_states: TSOcbStates = TSOcbStates()

    sensors: conlist(TSValue[bool], min_items=2, max_items=2) = [TSValue(value=False) for _ in range(2)]
    statuses: Statuses = Statuses()

    def mark_is_invalid_from_user(self):
        for rm_telemetry in self.rm_telemetry:
            rm_telemetry.mark_is_invalid_from_user()
        for sm_telemetry in self.sm_telemetry:
            sm_telemetry.mark_is_invalid_from_user()
        self.dsp_telemetry.mark_is_invalid_from_user()
        self.pwr_telemetry.mark_is_invalid_from_user()
        self.gps_telemetry.mark_is_invalid_from_user()
        for sfp_telemetry in self.sfp_telemetry:
            sfp_telemetry.mark_is_invalid_from_user()
        self.ocb_states.mark_is_invalid_from_user()

    def mark_is_invalid_from_server(self):
        for rm_telemetry in self.rm_telemetry:
            rm_telemetry.mark_is_invalid_from_server()
        for sm_telemetry in self.sm_telemetry:
            sm_telemetry.mark_is_invalid_from_server()
        self.dsp_telemetry.mark_is_invalid_from_server()
        self.pwr_telemetry.mark_is_invalid_from_server()
        self.gps_telemetry.mark_is_invalid_from_server()
        for sfp_telemetry in self.sfp_telemetry:
            sfp_telemetry.mark_is_invalid_from_server()
        self.ocb_states.mark_is_invalid_from_server()
